package org.example.tool;

import com.vividsolutions.jts.geom.Geometry;
import org.example.LandEntity;
import org.example.LogInterface;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.referencing.CRS;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;


import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ShapeTool {
    // 需要检查的字段列表
    private static final List<String> REQUIRED_FIELDS = Arrays.asList(
            "qhdm", "qhmc", "zwdm", "zwmc", "nhdm", "nhxm",
            "bxgsdm", "bxgsmc", "tbmj", "ct", "nf", "zzzb",
            "nhqqdm", "qqmj"
    );

    /**
     * 检查Shape文件是否采用WGS84坐标系（EPSG:4326）
     * @param shapefilePath Shape文件路径（.shp文件）
     * @return 如果是WGS84坐标系则返回true，否则返回false
     */
    public static boolean isWGS84(String shapefilePath) {
        File shapeFile = new File(shapefilePath);
        ShapefileDataStore dataStore = null;

        try {
            URL url = shapeFile.toURI().toURL();
            dataStore = new ShapefileDataStore(url);

            CoordinateReferenceSystem crs = dataStore.getSchema().getCoordinateReferenceSystem();

            if (crs == null) {
                System.out.println("Shape文件没有定义坐标参考系统");
                return false;
            }

            CoordinateReferenceSystem wgs84 = CRS.decode("EPSG:4326");
            System.out.println(wgs84.getName());
            return CRS.equalsIgnoreMetadata(crs, wgs84);

        } catch (MalformedURLException e) {
            e.printStackTrace();
            System.err.println("文件路径无效: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("处理Shape文件时出错: " + e.getMessage());
        } finally {
            if (dataStore != null) {
                dataStore.dispose();
            }
        }

        return false;
    }
    /**
     * 检查Shape文件是否采用WGS84坐标系（EPSG:4326）
     * @param shapefilePath Shape文件路径（.shp文件）
     * @return 检查结果信息
     */
    public static boolean checkCRS(String shapefilePath, LogInterface log) {
        File shapeFile = new File(shapefilePath);
        ShapefileDataStore dataStore = null;

        try {
            // 检查PRJ文件是否存在
            File prjFile = new File(shapefilePath.replace(".shp", ".prj"));

            if (!prjFile.exists()) {
                log.logError("错误: PRJ文件不存在，无法确定坐标系");
                return false;
            }

            // 使用ShapefileDataStoreFactory创建数据存储，设置编码
            Map<String, Serializable> params = new HashMap<>();
            params.put("url", shapeFile.toURI().toURL());
            params.put("charset", "GBK"); // 处理中文属性

            ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory();
            dataStore = (ShapefileDataStore) factory.createDataStore(params);

            // 方法1: 从schema获取CRS
            CoordinateReferenceSystem crs = dataStore.getSchema().getCoordinateReferenceSystem();

            // 如果方法1失败，尝试直接解析PRJ文件
            if (crs == null) {
                log.logWarn("通过Schema获取CRS失败，尝试直接解析PRJ文件...");
                // 读取PRJ文件内容
                String wkt = readPrjFile(prjFile);
                // 解析WKT字符串
//                if (wkt != null && !wkt.isEmpty()) {
//                    Parser parser = new Parser();
//                    crs = (CoordinateReferenceSystem) parser.parse(wkt);
//                }
                // 解析WKT字符串 - 使用CRS.parseWKT()替代原有的Parser方法
                if (wkt != null && !wkt.isEmpty()) {
                    String cleanedWkt = removeMetadataFromWkt(wkt);
                    if(cleanedWkt.contains("\"GCS_WGS_1984\"")){
                        return true;
                    }
                    System.out.println("WKT: " + cleanedWkt);
                    crs = CRS.parseWKT(cleanedWkt);
                }
            }

            if (crs == null) {
                log.logError("无法解析坐标系信息，PRJ文件可能格式不正确");
                return false;
            }

            // 获取EPSG编码
            String epsgCode = CRS.lookupIdentifier(crs, true);
//            System.out.println("EPSG编码: " + epsgCode);
//            // 检查是否为WGS84 (EPSG:4326)
//            boolean isWGS84 = "EPSG:4326".equals(epsgCode) ||
//                    CRS.equalsIgnoreMetadata(crs, CRS.decode("EPSG:4326"));
//
//            if(!isWGS84){
//                log.logError("坐标系不是WGS84，请检查PRJ文件或Shape文件坐标系定义");
//            }
            // 修改坐标系检查逻辑，增加容错处理
            boolean isWGS84 = false;
            try {
                System.out.println("EPSG编码: " + epsgCode);
                // 首先尝试直接比较EPSG代码
                if ("EPSG:4326".equals(epsgCode)||"CRS:84".equals(epsgCode)) {
                    isWGS84 = true;
                } else {
                    // 如果EPSG代码不匹配，尝试解析WGS84并比较
                    CoordinateReferenceSystem wgs84 = CRS.decode("EPSG:4326");
                    isWGS84 = CRS.equalsIgnoreMetadata(crs, wgs84);
                }
            } catch (FactoryException e) {
                log.logWarn("无法解析EPSG:4326坐标系，尝试其他方式验证...");
                // 添加备用验证方法，比如通过WKT字符串特征判断
                isWGS84 = checkWGS84ByWktCharacteristics(crs);
            }
            return isWGS84;

        } catch (Exception e) {
            log.logError("处理坐标系时出错: " + e.getMessage());
            e.printStackTrace();

            return false;
        } finally {
            if (dataStore != null) {
                dataStore.dispose();
            }
        }

    }
    // 增加备用方法：通过WKT特征判断是否为WGS84
    private static boolean checkWGS84ByWktCharacteristics(CoordinateReferenceSystem crs) {
        String wkt = crs.toWKT();
        // 检查WKT中是否包含WGS84的关键字
        return wkt != null &&
                (wkt.contains("WGS84") || wkt.contains("WGS 84")) &&
                wkt.contains("GeographicCRS");
    }
    /**
     * 读取PRJ文件内容
     */
    private static String readPrjFile(File prjFile) {
        try (Scanner scanner = new Scanner(prjFile, "UTF-8")) {
            return scanner.useDelimiter("\\A").next();
        } catch (Exception e) {
            System.err.println("读取PRJ文件失败: " + e.getMessage());
            return null;
        }
    }
    /**
     * 移除WKT字符串中的METADATA部分
     * @param wkt 原始WKT字符串
     * @return 清理后的WKT字符串
     */
    private static String removeMetadataFromWkt(String wkt) {
        if (wkt == null || !wkt.contains("METADATA")) {
            return wkt;
        }

        // 正则表达式模式匹配METADATA块
        // 匹配格式: METADATA[...] 或 METADATA [...]
        Pattern pattern = Pattern.compile(",METADATA\\s*\\[[^]]*\\]", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(wkt);

        // 替换匹配到的METADATA块
        String cleaned = matcher.replaceAll("");

        // 处理可能产生的多余逗号
        cleaned = cleaned.replaceAll(",\\s*,", ",").trim();
        if (cleaned.startsWith(",")) {
            cleaned = cleaned.substring(1).trim();
        }
        if (cleaned.endsWith(",")) {
            cleaned = cleaned.substring(0, cleaned.length() - 1).trim();
        }


        return cleaned;
    }
    /**
     * 检查Shape文件是否包含所有必需的字段
     * @param shapefilePath Shape文件路径（.shp文件）
     * @return 缺少的字段列表，如果全部存在则返回空列表
     */
    public static List<String> checkRequiredFields(String shapefilePath) {
        File shapeFile = new File(shapefilePath);
        ShapefileDataStore dataStore = null;
        List<String> missingFields = new ArrayList<>();

        try {
            Map<String, Serializable> params = new HashMap<>();
            params.put("url", shapeFile.toURI().toURL());
            params.put("charset", "GBK"); // 处理中文属性

            ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory();
            dataStore = (ShapefileDataStore) factory.createDataStore(params);

            // 获取Shape文件的属性描述
            SimpleFeatureType schema = dataStore.getSchema();
            List<AttributeDescriptor> attributes = schema.getAttributeDescriptors();

            // 提取所有字段名
            List<String> existingFields = new ArrayList<>();
            for (AttributeDescriptor attr : attributes) {
                existingFields.add(attr.getLocalName().toLowerCase()); // 转为小写以忽略大小写差异
            }

            // 检查每个必需字段是否存在
            for (String requiredField : REQUIRED_FIELDS) {
                if (!existingFields.contains(requiredField.toLowerCase())) {
                    missingFields.add(requiredField);
                }
            }

        } catch (MalformedURLException e) {
            System.err.println("文件路径无效: " + e.getMessage());
        } catch (Exception e) {
            System.err.println("处理Shape文件时出错: " + e.getMessage());
        } finally {
            if (dataStore != null) {
                dataStore.dispose();
            }
        }

        return missingFields;
    }

    public static HashSet<String> getDistinctFields(String shapefilePath,String field) {

        HashSet<String> distinctFields = new HashSet<>();
        //利用geotools读取每一个图形的指定属性字段
        File shapeFile = new File(shapefilePath);
        ShapefileDataStore dataStore = null;
        try {
            Map<String, Serializable> params = new HashMap<>();
            params.put("url", shapeFile.toURI().toURL());
            params.put("charset", "GBK");
            ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory();
            dataStore = (ShapefileDataStore) factory.createDataStore(params);
            SimpleFeatureSource featureSource = dataStore.getFeatureSource();
            SimpleFeatureCollection featureCollection = featureSource.getFeatures();
            SimpleFeatureIterator featureIterator = featureCollection.features();
            while (featureIterator.hasNext()) {
                SimpleFeature feature = featureIterator.next();
                Object value = feature.getAttribute(field);
                if (value != null) {
                    distinctFields.add(value.toString());
                }
            }
            return distinctFields;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (dataStore != null) {
                dataStore.dispose();
            }
        };
        return distinctFields;
    }

    public static boolean checkShapeFile(String shapefilePath,LogInterface  log) {
        // 检查坐标系
        boolean isWGS84 = checkCRS(shapefilePath,log);
        if (!isWGS84) {
            log.logError("❌ Shape文件不采用WGS84坐标系（EPSG:4326），请检查！");
            return false;
        }else {
            log.logSuccess("✅ Shape文件采用WGS84坐标系（EPSG:4326），检查通过！");
        }
        // 检查字段
        List<String> missingFields = checkRequiredFields(shapefilePath);
        if (!missingFields.isEmpty()) {
            log.logError("❌ Shape文件缺少以下必需字段：");
            for (String field : missingFields) {
                log.logError("  - " + field);
            }
            return false;
        }else {
            log.logSuccess("✅ Shape文件存在必需字段");
        }
        return true;
    }



    public static List<LandEntity> readShapeFile(String shapefilePath) throws IOException {
        List<LandEntity> lands = new ArrayList<>();
        File shapeFile = new File(shapefilePath);
        ShapefileDataStore dataStore = null;
            Map<String, Serializable> params = new HashMap<>();
            params.put("url", shapeFile.toURI().toURL());
            params.put("charset", "GBK");
            ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory();
            dataStore = (ShapefileDataStore) factory.createDataStore(params);
            SimpleFeatureSource featureSource = dataStore.getFeatureSource();
            SimpleFeatureCollection featureCollection = featureSource.getFeatures();
            SimpleFeatureIterator featureIterator = featureCollection.features();
            while (featureIterator.hasNext()) {
                SimpleFeature feature = featureIterator.next();
                LandEntity land = new LandEntity();
                // 将坐标读取为wkt格式
                land.setWkt(feature.getDefaultGeometryProperty().getValue().toString());
                //读取geojson
                GeometryJSON featureJson = new GeometryJSON();
                try (StringWriter writer = new StringWriter()) {
                    featureJson.write((Geometry) feature.getDefaultGeometryProperty().getValue(), writer);
                    land.setGeoJson(writer.toString());
                }
                //计算中心点
                com.vividsolutions.jts.geom.Geometry  geometry = (com.vividsolutions.jts.geom.Geometry) feature.getDefaultGeometryProperty().getValue();
                com.vividsolutions.jts.geom.Point centroid = geometry.getCentroid();
                land.setX(centroid.getX());
                land.setY(centroid.getY());

                land.setNhdm(feature.getAttribute("nhdm").toString());
                land.setQhmc(feature.getAttribute("qhmc").toString());
                land.setQhdm(feature.getAttribute("qhdm").toString());
                land.setZwdm(feature.getAttribute("zwdm").toString());
                land.setZwmc(feature.getAttribute("zwmc").toString());
                land.setNhxm(feature.getAttribute("nhxm").toString());
                land.setTbmj(new BigDecimal(feature.getAttribute("tbmj").toString()));
                land.setBxgsdm(feature.getAttribute("bxgsdm").toString());
                land.setBxgsmc(feature.getAttribute("bxgsmc").toString());
                land.setNf(feature.getAttribute("nf").toString());
                land.setZzzb(new BigDecimal(feature.getAttribute("zzzb").toString()));
                land.setCt(new BigDecimal(feature.getAttribute("ct").toString()));
                land.setNhqqdm(feature.getAttribute("nhqqdm").toString());
                land.setQqmj(new BigDecimal(feature.getAttribute("qqmj").toString()));
                lands.add(land);
            }
            dataStore.dispose();

        return lands;
    }


}
